/*
 * GraphUtil.cpp
 *
 *  Created on: 2011-8-5
 *      Author: terry
 */

#include "GraphUtil.h"
#include "CommonMacro.h"
#include "Strsafe.h"
#include <assert.h>
#include <Dvdmedia.h>



GraphUtil::GraphUtil()
{
}

GraphUtil::~GraphUtil()
{
}



HRESULT GraphUtil::getFriendlyName(IMoniker* pMoniker, VARIANT& var)
{
    IPropertyBag*  iBag = NULL;
	HRESULT hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&iBag);
	if (SUCCEEDED(hr))
	{
	    var.vt = VT_BSTR;
	    hr = iBag->Read(L"FriendlyName", &var, NULL);

        SAFE_RELEASE(iBag);
	}
    return hr;
}

HRESULT GraphUtil::getFriendlyName(IMoniker* pMoniker, WString& wstr)
{
    VARIANT var;
    HRESULT hr = getFriendlyName(pMoniker, var);
    if (SUCCEEDED(hr))
    {
        wstr.assign(var.bstrVal);
        SysFreeString(var.bstrVal);
    }
    return hr;
}

IEnumMoniker* getClassEnumerator(REFCLSID clsidDeviceClass)
{
    IEnumMoniker*	iEmMoniker = NULL;

    ICreateDevEnum* iCreateDevEnum = NULL;
	CoCreateInstance(CLSID_SystemDeviceEnum,
		NULL, CLSCTX_INPROC_SERVER,	IID_ICreateDevEnum,
		(void**)&iCreateDevEnum);

    if (iCreateDevEnum)
    {
        iCreateDevEnum->CreateClassEnumerator(clsidDeviceClass, &iEmMoniker, 0 );
        iCreateDevEnum->Release();
    }
    return iEmMoniker;
}


HRESULT GraphUtil::enumSystemDevice(REFCLSID clsidDeviceClass, WStringArray& array )
{
	IEnumMoniker*	iEmMoniker = getClassEnumerator(clsidDeviceClass);
    if (!iEmMoniker)
    {
        return E_FAIL;
    }

	iEmMoniker->Reset();
	IMoniker* pMoniker = NULL;
	ULONG cFetched = 0;
	while( true )
	{
		HRESULT hr = iEmMoniker->Next(1, &pMoniker, &cFetched);
		if (hr != S_OK )
		{
			break;
		}

        WString wstr;
        hr = getFriendlyName(pMoniker, wstr);
        if (SUCCEEDED(hr))
		{
            array.push_back(wstr);
        }

		SAFE_RELEASE(pMoniker);
	}

    SAFE_RELEASE(iEmMoniker);

	return S_OK;
}


// Adds a DirectShow filter graph to the Running Object Table,
// allowing GraphEdit to "spy" on a remote filter graph.
HRESULT GraphUtil::addGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)
{
	IMoniker * pMoniker;
	IRunningObjectTable *pROT;
	WCHAR wsz[128];
	HRESULT hr;

	if (!pUnkGraph || !pdwRegister)
		return E_POINTER;

	if (FAILED(GetRunningObjectTable(0, &pROT)))
		return E_FAIL;

	StringCchPrintfW(wsz, 128, L"FilterGraph %08x pid %08x\0", (DWORD_PTR)pUnkGraph,
			  GetCurrentProcessId());

	hr = CreateItemMoniker(L"!", wsz, &pMoniker);
	if (SUCCEEDED(hr))
	{
		// Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference
		// to the object.  Using this flag will cause the object to remain
		// registered until it is explicitly revoked with the Revoke() method.
		//
		// Not using this flag means that if GraphEdit remotely connects
		// to this graph and then GraphEdit exits, this object registration
		// will be deleted, causing future attempts by GraphEdit to fail until
		// this application is restarted or until the graph is registered again.
		hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph,
							pMoniker, pdwRegister);
		pMoniker->Release();
	}

	pROT->Release();
	return hr;
}

// Removes a filter graph from the Running Object Table
void GraphUtil::removeGraphFromRot(DWORD pdwRegister)
{
	IRunningObjectTable *pROT;

	if (SUCCEEDED(GetRunningObjectTable(0, &pROT)))
	{
		pROT->Revoke(pdwRegister);
		pROT->Release();
	}
}




HRESULT GraphUtil::saveGraphFile(IGraphBuilder *pGraph, const WCHAR *wszPath)
{
	const WCHAR wszStreamName[] = L"ActiveMovieGraph";
	HRESULT hr;

	IStorage *pStorage = NULL;
	hr = StgCreateDocfile(
		wszPath,
		STGM_CREATE | STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
		0, &pStorage);
	if(FAILED(hr))
	{
		return hr;
	}

	IStream *pStream;
	hr = pStorage->CreateStream(
		wszStreamName,
		STGM_WRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
		0, 0, &pStream);
	if (FAILED(hr))
	{
		pStorage->Release();
		return hr;
	}

	IPersistStream *pPersist = NULL;
	pGraph->QueryInterface(IID_IPersistStream, reinterpret_cast<void**>(&pPersist));
	hr = pPersist->Save(pStream, TRUE);
	pStream->Release();
	pPersist->Release();
	if (SUCCEEDED(hr))
	{
		hr = pStorage->Commit(STGC_DEFAULT);
	}
	pStorage->Release();
	return hr;
}


HRESULT GraphUtil::loadGraphFile(IGraphBuilder *pGraph, const WCHAR* wszName)
{
	IStorage *pStorage = 0;
	if (S_OK != StgIsStorageFile(wszName))
	{
		return E_FAIL;
	}
	HRESULT hr = StgOpenStorage(wszName, 0,
		STGM_TRANSACTED | STGM_READ | STGM_SHARE_DENY_WRITE,
		0, 0, &pStorage);
	if (FAILED(hr))
	{
		return hr;
	}
	IPersistStream *pPersistStream = 0;
	hr = pGraph->QueryInterface(IID_IPersistStream,
			 reinterpret_cast<void**>(&pPersistStream));
	if (SUCCEEDED(hr))
	{
		IStream *pStream = 0;
		hr = pStorage->OpenStream(L"ActiveMovieGraph", 0,
			STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
		if(SUCCEEDED(hr))
		{
			hr = pPersistStream->Load(pStream);
			pStream->Release();
		}
		pPersistStream->Release();
	}
	pStorage->Release();
	return hr;
}


void GraphUtil::deleteMediaType(AM_MEDIA_TYPE *pmt)
{
	// Allow NULL pointers for coding simplicity
	if (pmt == NULL) {
		return;
	}

	// Free media type's format data
	if (pmt->cbFormat != 0)
	{
		CoTaskMemFree((PVOID)pmt->pbFormat);

		// Strictly unnecessary but tidier
		pmt->cbFormat = 0;
		pmt->pbFormat = NULL;
	}

	// Release interface
	if (pmt->pUnk != NULL)
	{
		pmt->pUnk->Release();
		pmt->pUnk = NULL;
	}

	// Free media type
	CoTaskMemFree((PVOID)pmt);
}


//  Free an existing media type (ie free resources it holds)
void GraphUtil::freeMediaType(AM_MEDIA_TYPE& mt)
{
	if(mt.cbFormat != 0)
	{
		CoTaskMemFree((PVOID)mt.pbFormat);

		// Strictly unnecessary but tidier
		mt.cbFormat = 0;
		mt.pbFormat = NULL;
	}
	if(mt.pUnk != NULL)
	{
		mt.pUnk->Release();
		mt.pUnk = NULL;
	}
}



HRESULT GraphUtil::createFilter(REFCLSID rclsid, IBaseFilter **ppFilter)
{
	return CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IBaseFilter,(VOID**)ppFilter);
};


HRESULT GraphUtil::createFilter(const CLSID clsidDeviceClass, const WCHAR* szDevName, IBaseFilter **ppFilter)
{
    IEnumMoniker*	iEmMoniker = getClassEnumerator(clsidDeviceClass);
    if (!iEmMoniker)
    {
        return E_FAIL;
    }

    HRESULT hr = S_OK;
	bool found = false;
	iEmMoniker->Reset();
	IMoniker* pMoniker = NULL;
	ULONG cFetched = 0;
	while( !found )
	{
		hr = iEmMoniker->Next(1, &pMoniker, &cFetched);
		if (hr != S_OK )
		{
			break;
		}

        VARIANT var;
        hr = getFriendlyName(pMoniker, var);
        if (SUCCEEDED(hr))
        {
            int cmpResult = wcscmp( var.bstrVal, szDevName );
            SysFreeString(var.bstrVal);

            if ( cmpResult == 0 )
		    {
			    found = true;
			    hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)ppFilter);
		    }
        }

        SAFE_RELEASE(pMoniker);
	}

	if (SUCCEEDED(hr) && !found)
	{
		hr = E_FAIL;
	}

    SAFE_RELEASE(iEmMoniker);

	return hr;
}


HRESULT GraphUtil::createFilter(const CLSID clsidDeviceClass, const WCHAR* szDevName, int nIndex, IBaseFilter **ppFilter)
{
	IEnumMoniker*	iEmMoniker = getClassEnumerator(clsidDeviceClass);
    if (!iEmMoniker)
    {
        return E_FAIL;
    }

    HRESULT hr = S_OK;
	int deviceIndex = 0;
	bool found = false;
	iEmMoniker->Reset();
	IMoniker* pMoniker = NULL;
	ULONG cFetched = 0;
	while(!found)
	{
		hr = iEmMoniker->Next(1, &pMoniker, &cFetched);
		if (hr != S_OK )
		{
			break;
		}

        VARIANT var;
        hr = getFriendlyName(pMoniker, var);
        if (SUCCEEDED(hr))
        {
            int cmpResult = wcscmp( var.bstrVal, szDevName );
            SysFreeString(var.bstrVal);

            if ( cmpResult == 0 )
		    {
                if ( deviceIndex == nIndex )
                {
			        found = true;
			        hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)ppFilter);
                }
                deviceIndex ++;
		    }
        }

        SAFE_RELEASE(pMoniker);
	}

	if (SUCCEEDED(hr) && !found)
	{
		hr = E_FAIL;
	}

    SAFE_RELEASE(iEmMoniker);

	return hr;
}


void GraphUtil::removeAllFilters( IGraphBuilder* pBuilder )
{
	IEnumFilters* pEnum = NULL;
	if ( FAILED( pBuilder->EnumFilters(&pEnum) ) )
	{
		return;
	}

	IBaseFilter* pFilter = NULL;
	while (S_OK == pEnum->Next(1,&pFilter,0))
	{
		disconnectFilter(pBuilder, pFilter);

		pBuilder->RemoveFilter( pFilter );
		pFilter->Release();
    }

	SAFE_RELEASE(pEnum);
}

HRESULT GraphUtil::removeFilter(IGraphBuilder *pGB, LPCWSTR strName)
{
    HRESULT hr = S_OK;
	IBaseFilter* pFilter = NULL;
    pGB->FindFilterByName(strName, &pFilter);
	if (pFilter)
	{
		hr = pGB->RemoveFilter(pFilter);

		SAFE_RELEASE(pFilter);
	}
	return hr;
}


HRESULT GraphUtil::disconnectPin(IGraphBuilder *pGB, IPin* pPin)
{
	CheckPointer(pPin,E_POINTER);

	IPin* pConPin = NULL;
	HRESULT hr = pPin->ConnectedTo(&pConPin);
	if (pConPin)
	{
		hr = pGB->Disconnect(pPin);
		hr = pGB->Disconnect(pConPin);

        pConPin->Release();
	}
	return hr;
}

HRESULT GraphUtil::disconnectFilter(IGraphBuilder* pGB,IBaseFilter* pFilter)
{
	CheckPointer(pFilter,E_POINTER);

	HRESULT hr = S_OK;
	IEnumPins* iEnum = NULL;
	hr = pFilter->EnumPins(&iEnum);
	if (iEnum != NULL)
	{
		iEnum->Reset();

		ULONG lFetched = 0;
		IPin* iPinTmp = NULL;
		while ( S_OK == iEnum->Next(1,&iPinTmp,&lFetched) )
		{
			if (iPinTmp)
			{
				hr = disconnectPin(pGB, iPinTmp);

                SAFE_RELEASE(iPinTmp);
			}
		}

        iEnum->Release();
	}
	return hr;
}

HRESULT GraphUtil::disconnectAll(IGraphBuilder* pGB)
{
	IEnumFilters* iEnum = NULL;
	HRESULT hr = pGB->EnumFilters(&iEnum);
	FAILED_RETURN(hr);

	iEnum->Reset();

	IBaseFilter* iFilter = NULL;
	ULONG lFetched = 0;
	while ( hr = iEnum->Next(1, &iFilter, &lFetched), hr == S_OK )
	{
		//std::wstring name = getFilterName( iFilter );
		disconnectFilter( pGB, iFilter );

		iFilter->Release();
	}

	iEnum->Release();

	return hr;
};


///////////////////////////////////////////////////////////////////

HRESULT GraphUtil::connectPin( IGraphBuilder* pGB, IPin* pPinSrc,
                               IPin* pPinDest, bool direct,
                               const AM_MEDIA_TYPE *pmt )
{
	CheckPointer(pPinSrc,E_POINTER);
	CheckPointer(pPinDest,E_POINTER);

	if ( direct )
	{
		return pGB->ConnectDirect(pPinSrc,pPinDest,pmt);
	}
	return pGB->Connect(pPinSrc,pPinDest);
};


HRESULT GraphUtil::connectFilter(IGraphBuilder* pGB,IBaseFilter *pFilterSrc,
                            IBaseFilter *pFilterDest,
							int nSrcPin,
							int nDestPin,
							bool direct,
                            const AM_MEDIA_TYPE *pmt )
{
	IPin*	pPinSrc = getOutPin(pFilterSrc,nSrcPin);
	IPin*   pPinDest = getInPin(pFilterDest,nDestPin);

	HRESULT hr = connectPin( pGB,pPinSrc,pPinDest,direct,pmt );
    
    SAFE_RELEASE(pPinSrc);
    SAFE_RELEASE(pPinDest);

    return hr;
}

HRESULT GraphUtil::connectPin( IGraphBuilder* pGB, IBaseFilter *pFilterSrc,
							   int nSrcPin,
                            IPin *pPinDest,
                            bool direct,
							const AM_MEDIA_TYPE *pmt )
{
	IPin* pPinSrc = getOutPin(pFilterSrc,nSrcPin);
	HRESULT hr = connectPin(pGB,pPinSrc,pPinDest,direct,pmt);
    SAFE_RELEASE(pPinSrc);
    return hr;
}

HRESULT GraphUtil::connectPin(IGraphBuilder* pGB,IPin *pPinSrc,
                            IBaseFilter *pFilterDest, int nDestPin,
                            bool direct, const AM_MEDIA_TYPE *pmt )
{
	IPin*	pPinDest = getInPin(pFilterDest,nDestPin);
    HRESULT hr = connectPin(pGB,pPinSrc,pPinDest,direct,pmt);
    SAFE_RELEASE(pPinDest);
	return hr;
}



///////////////////////////////////////////////////////////////////

HRESULT GraphUtil::findRenderer(IGraphBuilder *pGB, const GUID *mediatype, IBaseFilter **ppFilter)
{
    CheckPointer(pGB, E_POINTER);
    CheckPointer(mediatype, E_POINTER);

	HRESULT hr;
	IEnumFilters *pEnum = NULL;
	IBaseFilter *pFilter = NULL;
	IPin *pPin;
	ULONG ulFetched, ulInPins, ulOutPins;
	BOOL bFound=FALSE;

	// Clear the filter pointer in case there is no match
	if (ppFilter)
		*ppFilter = NULL;

	// Get filter enumerator
	hr = pGB->EnumFilters(&pEnum);
	if (FAILED(hr))
		return hr;

	pEnum->Reset();

	// Enumerate all filters in the graph
	while(!bFound && (pEnum->Next(1, &pFilter, &ulFetched) == S_OK))
	{

		// Find a filter with one input and no output pins
		hr = countFilterPins(pFilter, &ulInPins, &ulOutPins);
		if (FAILED(hr))
        {
            SAFE_RELEASE(pFilter);
			break;
        }

		if (ulOutPins == 0)
		{
			// Get the first pin on the filter
			pPin = getInPin(pFilter, 0);

			// Read this pin's major media type
			AM_MEDIA_TYPE type={0};
			hr = pPin->ConnectionMediaType(&type);
            pPin->Release();

			if (FAILED(hr))
            {
                SAFE_RELEASE(pFilter);
				break;
            }

			// Is this pin's media type the requested type?
			// If so, then this is the renderer for which we are searching.
			// Copy the interface pointer and return.
			if (type.majortype == *mediatype)
			{
				// Found our filter
				*ppFilter = pFilter;
				bFound = TRUE;
			}

			// Delete memory allocated by ConnectionMediaType()
			freeMediaType(type);
		}

        SAFE_RELEASE(pFilter);

	}

	pEnum->Release();
	return hr;
}


HRESULT GraphUtil::findAudioRenderer(IGraphBuilder *pGB, IBaseFilter **ppFilter)
{
	return findRenderer(pGB, &MEDIATYPE_Audio, ppFilter);
}

HRESULT GraphUtil::findVideoRenderer(IGraphBuilder *pGB, IBaseFilter **ppFilter)
{
	return findRenderer(pGB, &MEDIATYPE_Video, ppFilter);
}


HRESULT GraphUtil::countFilterPins(IBaseFilter *pFilter, ULONG *pulInPins, ULONG *pulOutPins)
{
	HRESULT hr=S_OK;
	IEnumPins *pEnum=0;
	ULONG ulFound;
	IPin *pPin;

	// Verify input
	if (!pFilter || !pulInPins || !pulOutPins)
		return E_POINTER;

	// Clear number of pins found
	*pulInPins = 0;
	*pulOutPins = 0;

	// Get pin enumerator
	hr = pFilter->EnumPins(&pEnum);
	if(FAILED(hr))
		return hr;

	pEnum->Reset();

	// Count every pin on the filter
	while(S_OK == pEnum->Next(1, &pPin, &ulFound))
	{
		PIN_DIRECTION pindir = (PIN_DIRECTION) PINDIR_NONE;

		hr = pPin->QueryDirection(&pindir);

		if(pindir == PINDIR_INPUT)
			(*pulInPins)++;
		else
			(*pulOutPins)++;

		pPin->Release();
	}

	pEnum->Release();
	return hr;
}

HRESULT GraphUtil::getPin( IBaseFilter * pFilter, PIN_DIRECTION dirrequired, int iNum, IPin **ppPin)
{
	CheckPointer( pFilter, E_POINTER );

	IEnumPins* pEnum = NULL;
	*ppPin = NULL;


	HRESULT hr = pFilter->EnumPins(&pEnum);
	if(FAILED(hr))
	{
		return hr;
	}

	ULONG ulFound = 0;
	IPin *pPin;
	hr = E_FAIL;

	while(S_OK == pEnum->Next(1, &pPin, &ulFound))
	{
		PIN_DIRECTION pindir = (PIN_DIRECTION)PINDIR_NONE;

		pPin->QueryDirection(&pindir);
		if( pindir == dirrequired )
		{
			if(iNum == 0)
			{
				*ppPin = pPin;  // Return the pin's interface
				hr = S_OK;      // Found requested pin, so clear error
				break;
			}
			iNum--;
		}

		pPin->Release();
	}

    pEnum->Release();

	return hr;
}

IPin* GraphUtil::getInPin( IBaseFilter * pFilter, int nPin )
{
	IPin* pPin = NULL;
	getPin( pFilter, PINDIR_INPUT, nPin, &pPin );
	return pPin;
}

IPin* GraphUtil::getOutPin( IBaseFilter * pFilter, int nPin )
{
	IPin* pPin = NULL;
	getPin( pFilter, PINDIR_OUTPUT, nPin, &pPin );
	return pPin;
}

HRESULT GraphUtil::getPin( IBaseFilter * pFilter, PIN_DIRECTION dirrequired, LPCWSTR strName, IPin **ppPin)
{
	CheckPointer(pFilter,E_POINTER);
	IEnumPins* pEnum = NULL;

	HRESULT hr = pFilter->EnumPins(&pEnum);
	if(FAILED(hr))
    {
		return hr;
    }

	IPin* pPin = NULL;
	BOOL bEmptyStr = FALSE;
	if ((strName == NULL) || (wcslen(strName) == 0))
		bEmptyStr = TRUE;

	bool found = false;
	while(!found && S_OK == pEnum->Next(1, &pPin, 0))
	{

		PIN_INFO pinInfo;
		pPin->QueryPinInfo(&pinInfo);
		if (SUCCEEDED(hr))
		{

			if ( (dirrequired == pinInfo.dir) || (dirrequired == PINDIR_NONE) )
			{
				if(bEmptyStr)
				{
					*ppPin = pPin;  // Return the pin's interface
					pPin->AddRef();
					hr = S_OK;      // Found requested pin, so clear error
					found = true;
				}
				else if (wcscmp(strName,pinInfo.achName) == 0)
				{
					*ppPin = pPin;  // Return the pin's interface
					pPin->AddRef();
					hr = S_OK;      // Found requested pin, so clear error
					found = true;
				}
			}

			SAFE_RELEASE(pinInfo.pFilter);
		}
		SAFE_RELEASE( pPin );
	}

    SAFE_RELEASE(pEnum);

	return hr;
}

IPin* GraphUtil::getInPin(IBaseFilter * pFilter,LPCWSTR strName)
{
	IPin* pComPin = NULL;
	getPin(pFilter, PINDIR_INPUT,strName, &pComPin);
	return pComPin;
}

IPin* GraphUtil::getOutPin(IBaseFilter * pFilter,LPCWSTR strName)
{
    IPin* pComPin = NULL;
	getPin(pFilter, PINDIR_OUTPUT, strName, &pComPin);
	return pComPin;
}

HRESULT GraphUtil::getUnconnectedPin(
    IBaseFilter *pFilter,
    PIN_DIRECTION PinDir,
    IPin **ppPin)
{
    *ppPin = 0;
    IEnumPins *pEnum = 0;
    IPin *pPin = 0;
    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        return hr;
    }

    hr = E_FAIL;
    while (pEnum->Next(1, &pPin, NULL) == S_OK)
    {
        PIN_DIRECTION ThisPinDir;
        pPin->QueryDirection(&ThisPinDir);
        if (ThisPinDir == PinDir)
        {
            IPin *pTmp = 0;
            pPin->ConnectedTo(&pTmp);
            if (pTmp)
            {
                pTmp->Release();
            }
            else  // Unconnected, this is the pin we want.
            {
                *ppPin = pPin;
                hr = S_OK;
                break;
            }
        }
        pPin->Release();
    }
    pEnum->Release();
    // Did not find a matching pin.
    return hr;
}


HRESULT	GraphUtil::getPinByMajorType(IBaseFilter * pFilter, GUID guid,
                                     PIN_DIRECTION dirrequired,IPin** ppPin)
{
	HRESULT hr = S_FALSE;
    IEnumPins* pEnum = NULL;
    hr = pFilter->EnumPins(&pEnum);
    
    if (!pEnum)
    {
        return hr;
    }

    IPin* pPin = NULL;
    while (pEnum->Next(1, &pPin, NULL) == S_OK)
    {
        PIN_DIRECTION ThisPinDir;
        pPin->QueryDirection(&ThisPinDir);
        if (ThisPinDir == dirrequired)
        {
            AM_MEDIA_TYPE* pType = NULL;
            hr = getPinMedia(pPin, guid, FALSE, &pType);
            if (SUCCEEDED(hr))
            {
                deleteMediaType(pType);

                *ppPin = pPin;
                break;
            }
        }
        pPin->Release();
    }

    pEnum->Release();

	return hr;
}

HRESULT GraphUtil::getPin(IBaseFilter* pFilter, PIN_DIRECTION pinDir,
                          GraphUtil::WStringArray& arrName)
{
    HRESULT hr = S_FALSE;
    IEnumPins* pEnum = NULL;
    hr = pFilter->EnumPins(&pEnum);
    CheckPointer(pEnum, hr);

    IPin* pPin = NULL;
    while (pEnum->Next(1, &pPin, NULL) == S_OK)
    {
        PIN_DIRECTION ThisPinDir;
        pPin->QueryDirection(&ThisPinDir);
        if (ThisPinDir == pinDir)
        {
            PIN_INFO info;
            memset(&info, 0, sizeof(info));
            pPin->QueryPinInfo(&info);
            arrName.push_back(info.achName);
            SAFE_RELEASE(info.pFilter);
        }
        pPin->Release();
    }

    pEnum->Release();

	return hr;
}



GraphUtil::WString GraphUtil::getPinName(IPin* pPin)
{
	WString name;
	PIN_INFO pinInfo;
	HRESULT hr = pPin->QueryPinInfo(&pinInfo);
	if (SUCCEEDED(hr))
	{
		name.assign( pinInfo.achName );
		SAFE_RELEASE(pinInfo.pFilter);
	}
	return name;
}


HRESULT	GraphUtil::getConnectedFilter(IPin* pSrcPin,IBaseFilter** ppFilter)
{
	IPin* pDestPin;
	HRESULT hr = pSrcPin->ConnectedTo(&pDestPin);
	if (pDestPin)
	{
		PIN_INFO	info;
		hr = pDestPin->QueryPinInfo(&info);
		if (SUCCEEDED(hr))
		{
			*ppFilter = info.pFilter;
		}
        pDestPin->Release();
	}
	return hr;
}

bool GraphUtil::isPinConnected( IPin* pPin )
{
    bool connected = false;
	IPin* pComPin = NULL;
	pPin->ConnectedTo(&pComPin);
    if (pComPin)
    {
        connected = true;
        pComPin->Release();
    }
	return connected;
}

HRESULT GraphUtil::getPinMedia(IPin* pPin,GUID guid,BOOL bExcept,AM_MEDIA_TYPE** ppMedia)
{
	CheckPointer(pPin,E_POINTER);
	CheckPointer(ppMedia,E_POINTER);
	HRESULT hr = S_FALSE;
	IEnumMediaTypes* pEnumMt = NULL;
	hr = pPin->EnumMediaTypes(&pEnumMt);
	if (pEnumMt)
	{
		hr = E_FAIL;
		pEnumMt->Reset();

		ULONG Fetched = 0;
		AM_MEDIA_TYPE *pMediaType;
		while (S_OK == pEnumMt->Next(1, &pMediaType, &Fetched))
		{

			if ( bExcept == (BOOL)(pMediaType->majortype != guid) )
			{
				*ppMedia = pMediaType;
				hr = S_OK;
				break;
			}
			else
			{
				deleteMediaType(pMediaType);
			}
		}

        pEnumMt->Release();
	}
	return hr;
}



bool GraphUtil::isSupportPropertyPage(IBaseFilter *pFilter)
{
	bool success = false;
	ISpecifyPropertyPages *pSpecify;
	// Discover if this filter contains a property page
	HRESULT hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
	if (SUCCEEDED(hr))
	{
		pSpecify->Release();
		success = true;
	}
	return success;
}

//��ʾFilter������ҳ
HRESULT GraphUtil::showFilterPropertyPage(IBaseFilter *pFilter, HWND hwndParent)
{
	HRESULT hr;
	ISpecifyPropertyPages *pSpecify=0;

	if (!pFilter)
		return E_NOINTERFACE;

	// Discover if this filter contains a property page
	hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpecify);
	if (SUCCEEDED(hr))
	{
		do
		{
			FILTER_INFO FilterInfo;
			hr = pFilter->QueryFilterInfo(&FilterInfo);
			if (FAILED(hr))
				break;

			CAUUID caGUID;
			hr = pSpecify->GetPages(&caGUID);
			if (FAILED(hr))
				break;

			// Display the filter's property page
			OleCreatePropertyFrame(
				hwndParent,             // Parent window
				0,                      // x (Reserved)
				0,                      // y (Reserved)
				FilterInfo.achName,     // Caption for the dialog box
				1,                      // Number of filters
				(IUnknown **)&pFilter,  // Pointer to the filter
				caGUID.cElems,          // Number of property pages
				caGUID.pElems,          // Pointer to property page CLSIDs
				0,                      // Locale identifier
				0,                      // Reserved
				NULL                    // Reserved
			);

			CoTaskMemFree(caGUID.pElems);
			FilterInfo.pGraph->Release();

		} while(0);
	}

	// Release interfaces
	if (pSpecify)
		pSpecify->Release();

	pFilter->Release();
	return hr;
}






HRESULT GraphUtil::grab( IBasicVideo* pBasicVideo, LPCTSTR strFile )
{
    CheckPointer( pBasicVideo, E_POINTER );

    long lSize = 0;
    HRESULT hr = pBasicVideo->GetCurrentImage( &lSize, 0 );
    if ( SUCCEEDED(hr) )
    {
        unsigned char* buf = new unsigned char [lSize];
        hr = pBasicVideo->GetCurrentImage( &lSize, (long*)buf );
        if ( SUCCEEDED(hr) )
        {
            BITMAPFILEHEADER  hdr;
            LPBITMAPINFOHEADER   lpbi;
            lpbi = (LPBITMAPINFOHEADER)buf;
            int nColors = 1 << lpbi->biBitCount;
            if (nColors > 256)
                nColors = 0;

            hdr.bfType    = ((WORD) ('M' << 8) | 'B');    //always is "BM"
            hdr.bfSize    = lSize + sizeof( hdr );
            hdr.bfReserved1   = 0;
            hdr.bfReserved2   = 0;
            hdr.bfOffBits     = (DWORD) (sizeof(BITMAPFILEHEADER) );// + lpbi->biSize;

			FILE* f = NULL;
			fopen_s(&f, strFile, "w+b");
            if ( f )
            {
                fwrite( &hdr, sizeof(char), sizeof(BITMAPFILEHEADER), f );
                fwrite( buf, sizeof(char), lSize, f );
                fclose( f );
            }
        }

        delete[] buf;
    }
    return hr;
}


#define BFT_BITMAP 0x4d42   /* 'BM' */
#define DibNumColors(lpbi)      ((lpbi)->biClrUsed == 0 && (lpbi)->biBitCount <= 8 \
                                    ? (int)(1 << (int)(lpbi)->biBitCount)          \
                                    : (int)(lpbi)->biClrUsed)

#define DibSize(lpbi)           ((lpbi)->biSize + (lpbi)->biSizeImage + (int)(lpbi)->biClrUsed * sizeof(RGBQUAD))

#define DibPaletteSize(lpbi)    (DibNumColors(lpbi) * sizeof(RGBQUAD))

bool GraphUtil::writeImage( BYTE* pDib, LPCTSTR filename )
{
	BITMAPFILEHEADER    hdr;
    DWORD               dwSize, dwWritten;
    LPBITMAPINFOHEADER  pdib = (LPBITMAPINFOHEADER) pDib;
	HANDLE hFile = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL,
                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

    if (hFile == INVALID_HANDLE_VALUE)
        return false;

    // Initialize the bitmap header
    dwSize = DibSize(pdib);
    hdr.bfType          = BFT_BITMAP;
    hdr.bfSize          = dwSize + sizeof(BITMAPFILEHEADER);
    hdr.bfReserved1     = 0;
    hdr.bfReserved2     = 0;
    hdr.bfOffBits       = (DWORD)sizeof(BITMAPFILEHEADER) + pdib->biSize +
        DibPaletteSize(pdib);

    // Write the bitmap header and bitmap bits to the file
    WriteFile(hFile, (LPCVOID) &hdr, sizeof(BITMAPFILEHEADER), &dwWritten, 0);
    WriteFile(hFile, (LPCVOID) pdib, dwSize, &dwWritten, 0);

    // Close the file
    CloseHandle(hFile);
	return true;
}

HRESULT GraphUtil::grab( IVMRWindowlessControl* pControl, LPCTSTR filename )
{
	CheckPointer( pControl, E_POINTER );
	BYTE* pDib = NULL;
	HRESULT hr = pControl->GetCurrentImage( &pDib );
	if ( SUCCEEDED(hr) )
	{
		if ( !writeImage( pDib, filename ) )
		{
			hr = E_FAIL;
		}
		CoTaskMemFree(pDib);
	}
	return hr;
}


GraphUtil::WString GraphUtil::getFilterFile(REFCLSID clsid )
{
	WString filename;

	HRESULT hr;
	LPOLESTR strCLSID;

	// Convert binary CLSID to a readable version
	hr = StringFromCLSID(clsid, &strCLSID);
	if(SUCCEEDED(hr))
	{
		WCHAR szKey[512];

		// Create key name for reading filename registry
		StringCchPrintfW(szKey, 512, L"Software\\Classes\\CLSID\\%s\\InprocServer32\0",
				 strCLSID);

		// Free memory associated with strCLSID (allocated in StringFromCLSID)
		CoTaskMemFree(strCLSID);

		HKEY hkeyFilter=0;
		DWORD dwSize=MAX_PATH;
		int rc=0;
		// Open the CLSID key that contains information about the filter
		rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, szKey, &hkeyFilter);
		if (rc == ERROR_SUCCESS)
		{
			BYTE szFile[MAX_PATH];
			rc = RegQueryValueExW(hkeyFilter, NULL,  // Read (Default) value
								 NULL, NULL, szFile, &dwSize);

			if (rc == ERROR_SUCCESS)
			{
				filename.assign( (WCHAR*)szFile );
			}

			RegCloseKey(hkeyFilter);
		}
	}
	return filename;
}



HRESULT GraphUtil::getVideoInfo(AM_MEDIA_TYPE* pmt, long& width, long& height, LONGLONG& timePerFrame)
{
    HRESULT hr = E_INVALIDARG;
    if (pmt->formattype == FORMAT_VideoInfo)
    {
        VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;
        width = pVIH->bmiHeader.biWidth;
        height = abs(pVIH->bmiHeader.biHeight);
        timePerFrame = pVIH->AvgTimePerFrame;
        hr = S_OK;
    }
    else if (pmt->formattype == FORMAT_VideoInfo2)
    {
        VIDEOINFOHEADER2 *pVIH2 = (VIDEOINFOHEADER2*)pmt->pbFormat;
        width = pVIH2->bmiHeader.biWidth;
        height = abs(pVIH2->bmiHeader.biHeight);
        timePerFrame = pVIH2->AvgTimePerFrame;
        hr = S_OK;
    }
    else
    {
        // pass
    }
    return hr;
}

HRESULT GraphUtil::setVideoInfo(AM_MEDIA_TYPE* pmt, long width, long height, LONGLONG timePerFrame)
{
    HRESULT hr = E_INVALIDARG;
    if (pmt->formattype == FORMAT_VideoInfo)
    {
        VIDEOINFOHEADER *pVIH = (VIDEOINFOHEADER*)pmt->pbFormat;

        if (width > 0 && height > 0)
        {
            pVIH->bmiHeader.biWidth = width;
            pVIH->bmiHeader.biHeight = height;
            pVIH->bmiHeader.biSizeImage = 
                width * abs(height) * (pVIH->bmiHeader.biBitCount/8);
            pmt->lSampleSize = pVIH->bmiHeader.biSizeImage;
        }

        if (timePerFrame > 0)
        {
            pVIH->AvgTimePerFrame = timePerFrame;
        }


        hr = S_OK;
    }
    else if (pmt->formattype == FORMAT_VideoInfo2)
    {
        VIDEOINFOHEADER2 *pVIH2 = (VIDEOINFOHEADER2*)pmt->pbFormat;
        if (width > 0 && height > 0)
        {
            pVIH2->bmiHeader.biWidth = width;
            pVIH2->bmiHeader.biHeight = height;
            pVIH2->dwPictAspectRatioX = width;
            pVIH2->dwPictAspectRatioY = height;
            pVIH2->bmiHeader.biSizeImage = 
                width * abs(height) * (pVIH2->bmiHeader.biBitCount/8);
            pmt->lSampleSize = pVIH2->bmiHeader.biSizeImage;
        }

        if (timePerFrame > 0)
        {
            pVIH2->AvgTimePerFrame = timePerFrame;
        }

        hr = S_OK;
    }
    else
    {
        // pass
    }
    return hr;
}